package epoch.game.core.distr.service;

import java.lang.reflect.Method;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import epoch.game.core.action.IAction;
import epoch.game.core.actor.ATimer;
import epoch.game.core.actor.Actor;
import epoch.game.core.context.Context;
import epoch.game.core.distr.Node;
import epoch.game.core.distr.NodeType;
import epoch.game.core.distr.call.Call;
import epoch.game.core.timer.GameTimer;

/**
 * 服务基类
 * 
 * @author will
 * @date 2016年3月25日 上午10:47:41
 */
public abstract class Service implements IService{

	protected Logger log = LogManager.getLogger(this.getClass().getName());
	
	private int serviceId;
	// 所属节点
	protected Node node;

	// 服务状态
	protected ServiceStatus status = ServiceStatus.NOT_INITED;
	// 开启时间
	long startTime = 0;
	
	private Actor[] workers;
	// 结点连接通知
	private boolean nodeNotify = false;
	
	public Service(){
		
	}
	
	public Service(Node node){
		this.node = node;
	}

	public Node getNode() {
		return node;
	}

	public void setNode(Node node) {
		this.node = node;
	}
	
	public void setNodeNotify(boolean need) {
		this.nodeNotify = need;
	}
	
	@Override
	public boolean getNodeNotify() {
		return this.nodeNotify;
	}
	
	@Override
	public int getId() {
		return serviceId;
	}

	/**
	 * 添加任务到线程执行
	 * @param action
	 */
	public void addAction(IAction action) {
		workers[0].send(action);
	}
	
	public void addAction(short methodKey, int callFlag, IAction action) {
		Actor worker = getWorker(methodKey, callFlag);
		worker.send(action);
	}
	
	public ServiceStatus getStatus(){
		return status;
	}
	
	
	@Override
	public void init(int serviceId) throws Exception{
		init(serviceId, 0, 1);
	}
	
	public void init(int serviceId, int tickPeriod) throws Exception{
		init(serviceId, tickPeriod, 1);
	}
	
	public void init(int serviceId, int tickPeriod, int nWorker) throws Exception{
		this.serviceId = serviceId;
		
		if (nWorker <= 0)
			nWorker = 1;
		this.workers = new Actor[nWorker];
		for (int i=0; i<nWorker; ++i) {
			this.workers[i] = Context.createActor(new Actor(), "Service-"+this.getClass().getName()+"-"+(i+1));
		}
		
		if (tickPeriod > 0) {
			this.workers[0].regTimer(new ATimer(tickPeriod) {
				protected void doAction() {
					if (ServiceStatus.STARTED == status && node.isAvailable()) {
						tick(System.currentTimeMillis());
					}
				 }
			});
		}
		//注册服务		
		Node.registerService(this);
		status = ServiceStatus.INITED;
	}
	
	public boolean start() {
		status = ServiceStatus.STARTING;
		startTime = System.currentTimeMillis();
		//Context.doAction(workers[0], ()->{onStart();return null;}, (obj)->{status = ServiceStatus.STARTED;});
		
		boolean ok = Context.callBlock(workers[0], ()-> {
			onServiceStart();
			return true;
		});
		if (ok) {
			status = ServiceStatus.STARTED;
		}
		return ok;
	}
	
	/**
	 * 服务启动
	 */
	protected void onServiceStart() {
	}
	
	public void stop() {
		status = ServiceStatus.STOPING;
		startTime = 0;
		Context.callBlock(workers[0], ()-> {
			onStop();
			return null;
		});
		Context.destroyActor(workers[0]);
		status = ServiceStatus.STOPED;
	}
	
	protected void onStop() {
	}
	
	public void tick(long nowTime) {	
	}

	@Override
	public void onNodeRegister(int nodeId, String nodeName, String host, int port, NodeType type) {
		
	}
	
	@Override
	public void onNodeUnregister(int nodeId, NodeType type) {
		
	}
	
	/**
	 * 根据方法名称获取对应的方法key值
	 * @param name
	 * @return
	 */
	public int getMethodKey(Method method){
		return method.hashCode();
	}
	
	@Override
	public void doCall(Call call) {
		if (ServiceStatus.STARTED != status) {
			log.error("doCall failed:service not started! call=" + call);
			return;
		}
		Actor worker = getWorker(call.methodKey, (int)call.callFlag);
		if (worker == null) {
			log.error("doCall failed:service threadContext is null! call=" + call);
			return;
		}	
		call.service = this;
		worker.call(call);
	}
	
	/**
	 * 获取工作线程
	 * @param callFlag
	 * @return
	 */
	protected Actor getWorker(short methodKey, int callFlag){
		if (callFlag < 0) {
			callFlag = Math.abs(callFlag);
		}
		int hit = callFlag % workers.length;
		return workers[hit];
	}

	/**
	 * 设置返回值,只能在服务内返回
	 * @param returns
	 */
	public void returns(Object returns){
		Context.returns(returns);
	}
	
	/**
	 * 设置返回值,格式如：("paramName1",paramVal1,"paramName2",paramVal2)<br />
	 * 对应回调方法的格式应该为 callback(Param returns, Param context)
	 * @param returns
	 */
	public void returns(Object... returns) {
		Context.returns(returns);
	}
	
	public boolean regTimer(GameTimer timer) {
		workers[0].regTimer(timer);
		return true;
	}
	public int sourceNodeId() {
		return currentCall().fromNodeId;
	}
	public Call currentCall() {
		return Context.currentCall();
	}
}
